﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Transactions;
using System.Linq.Expressions;
using System.Reflection;
using BHXH.LinqRepository;

namespace BHXH.LinqRepository
{

    public class TempRepository
    {
        public static BHXH2013DBDataContext dataContext=null;
    }

    public class Repository<T> : IDisposable, IRepository<T> where T : class, IDbTable
    {
        protected  BHXH2013DBDataContext dataContext = null;
        private bool _disposed;

        public Repository()
        {
            // dataContext = new BHXH2013DBDataContext(LocalSettings.ConnectString);
            if (TempRepository.dataContext == null)
            {
                dataContext = new BHXH2013DBDataContext(BHXH.Repository.LocalSettings.ConnectString);
                TempRepository.dataContext = dataContext;
            }
            else
            {
                dataContext = TempRepository.dataContext;
            }

            _disposed = false;
        }

        public void Dispose()
        {
            Dispose(true);
            // Use SupressFinalize in case a subclass
            // of this type implements a finalizer.
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            // If you need thread safety, use a lock around these 
            // operations, as well as in your methods that use the resource.
            if (!_disposed)
            {
                if (disposing)
                {
                    if (dataContext != null)
                        dataContext.Dispose();
                }

                // Indicate that the instance has been disposed.
                dataContext = null;
                _disposed = true;
            }
        }

        public void ClearCache()
        {
            dataContext.GetType().InvokeMember("ClearCache",
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
                null, dataContext, null);
        }

        public virtual T Get(int id)
        {
            var itemParameter = Expression.Parameter(typeof(T), "item");

            var whereExpression = Expression.Lambda<Func<T, bool>>
                (
                Expression.Equal(
                    Expression.Property(
                        itemParameter,
                        typeof(T).GetPrimaryKey().Name
                        ),
                    Expression.Constant(id)
                    ),
                new[] { itemParameter }
                );

            var item = GetTable().Where(whereExpression).SingleOrDefault();

            //if (item == null)
            //{
            //    throw new PrimaryKeyNotFoundException(string.Format("No {0} with primary key {1} found",
            //                                                        typeof(T).FullName, id));
            //}

            return item;
        }

        public virtual List<T> GetAll(int start, int limit, out int count)
        {
            count = dataContext.GetTable<T>().Count();
            return dataContext.GetTable<T>().Skip(start).Take(limit).ToList();
        }

        public virtual List<T> GetAll()
        {
            return dataContext.GetTable<T>().ToList();
        }

        public virtual void Add(T entity)
        {
            GetTable().InsertOnSubmit(entity);
        }
        public virtual void AddAll(List<T> entity)
        {
            GetTable().InsertAllOnSubmit(entity);
        }
        public virtual void Update(T entity)
        {
            GetTable().Attach(entity);
            dataContext.Refresh(RefreshMode.KeepCurrentValues, entity);
        }

        public virtual void UpdateAll(List<T> entities)
        {
            GetTable().AttachAll(entities);
            dataContext.Refresh(RefreshMode.KeepCurrentValues, entities);
        }

        public virtual void DeleteOnSubmit(T entity, bool logicalDelete)
        {
            if (logicalDelete)
            {
                entity.ModifiedDate = DateTime.Now;
            }
            else
                GetTable().DeleteOnSubmit(entity);
        }

        public virtual void DeleteOnSubmit(T entity)
        {
            DeleteOnSubmit(entity, true);
        }

        public virtual void Delete(int id)
        {
            DeleteOnSubmit(Get(id), false);
        }

        public virtual void DeleteAllOnSubmit(IEnumerable<T> entities, bool logicalDelete)
        {
            if (logicalDelete)
            {
                DateTime current = DateTime.Now;
                foreach (var entity in entities)
                {
                    entity.ModifiedDate = current;
                }
            }
            else
                GetTable().DeleteAllOnSubmit(entities);
        }

        public virtual void DeleteAllOnSubmit(IEnumerable<T> entities)
        {
            DeleteAllOnSubmit(entities, true);
        }

        public virtual void SubmitChanges()
        {
            try
            {
                dataContext.SubmitChanges(System.Data.Linq.ConflictMode.ContinueOnConflict);
            }
            catch
            {
                foreach (System.Data.Linq.ObjectChangeConflict occ in dataContext.ChangeConflicts)
                {
                    occ.Resolve(System.Data.Linq.RefreshMode.KeepChanges);
                }
                dataContext.SubmitChanges(System.Data.Linq.ConflictMode.FailOnFirstConflict);
            }
        }

        public virtual Table<T> GetTable()
        {
            return dataContext.GetTable<T>();
        }

    }

}